home *** CD-ROM | disk | FTP | other *** search
- /* Tests on logic */
-
- /* Small theorem prover for propositional logic, based on the
- * resolution principle.
- * Written by Ayal Pinkus
- * Version 0.1 initial implementation.
- *
- *
- * Examples:
- CanProve(( (a=>b) And (b=>c)=>(a=>c) )) <-- True
- CanProve(a Or Not a) <-- True
- CanProve(True Or a) <-- True
- CanProve(False Or a) <-- a
- CanProve(a And Not a) <-- False
- CanProve(a Or b Or (a And b)) <-- a Or b
- */
-
- RuleBase("=>",{a,b});
-
-
- /*
- Simplify a boolean expression. CNF is responsible
- for converting an expression to the following form:
- (p1 Or p2 Or ...) And (q1 Or q2 Or ...) And ...
- That is, a conjunction of disjunctions.
- */
-
-
- // Trivial simplifications
- 10 # CNF( Not True) <-- False;
- 11 # CNF( Not False) <-- True;
- 12 # CNF(True And (_x)) <-- CNF(x);
- 13 # CNF(False And (_x)) <-- False;
- 14 # CNF(_x And True) <-- CNF(x);
- 15 # CNF(_x And False) <-- False;
- 16 # CNF(True Or (_x)) <-- True;
- 17 # CNF(False Or (_x)) <-- CNF(x);
- 18 # CNF((_x) Or True ) <-- True;
- 19 # CNF((_x) Or False) <-- CNF(x);
-
- // A bit more complext
- 21 # CNF(_x Or _x) <-- CNF(x);
- 22 # CNF(_x And _x) <-- CNF(x);
- 23 # CNF(_x Or Not (_x)) <-- True;
- 14 # CNF(Not (_x) Or _x) <-- True;
- 25 # CNF(_x And Not (_x)) <-- False;
- 26 # CNF(Not (_x) And _x) <-- False;
-
- // Simplifications that deal with (in)equalities
- 25 # CNF(((_x) == (_y)) Or ((_x) !== (_y))) <-- True;
- 25 # CNF(((_x) !== (_y)) Or ((_x) == (_y))) <-- True;
- 26 # CNF(((_x) == (_y)) And ((_x) !== (_y))) <-- False;
- 26 # CNF(((_x) !== (_y)) And ((_x) == (_y))) <-- False;
-
- 27 # CNF(((_x) >= (_y)) And ((_x) < (_y))) <-- False;
- 27 # CNF(((_x) < (_y)) And ((_x) >= (_y))) <-- False;
- 28 # CNF(((_x) >= (_y)) Or ((_x) < (_y))) <-- True;
- 28 # CNF(((_x) < (_y)) Or ((_x) >= (_y))) <-- True;
-
- // some things that are more complex
- 120 # CNF((_x) Or (_y)) <-- LogOr(x, y, CNF(x), CNF(y));
- 10 # LogOr(_x,_y,_x,_y) <-- x Or y;
- 20 # LogOr(_x,_y,_u,_v) <-- CNF(u Or v);
-
- 130 # CNF( Not (_x)) <-- LogNot(x, CNF(x));
- 10 # LogNot(_x, _x) <-- Not (x);
- 20 # LogNot(_x, _y) <-- CNF(Not (y));
-
- 40 # CNF( Not ( Not (_x))) <-- CNF(x); // eliminate double negation
- 45 # CNF((_x)=>(_y)) <-- CNF((Not (x)) Or (y)); // eliminate implication
-
- 50 # CNF( Not ((_x) And (_y))) <-- CNF((Not x) Or (Not y)); // De Morgan's law
- 60 # CNF( Not ((_x) Or (_y))) <-- CNF(Not (x)) And CNF(Not (y)); // De Morgan's law
-
- /*
- 70 # CNF((_x) And ((_y) Or (_z))) <-- CNF(x And y) Or CNF(x And z);
- 70 # CNF(((_x) Or (_y)) And (_z)) <-- CNF(x And z) Or CNF(y And z);
-
- 80 # CNF((_x) Or ((_y) And (_z))) <-- CNF(x Or y) And CNF(x Or z);
- 80 # CNF(((_x) And (_y)) Or (_z)) <-- CNF(x Or z) And CNF(y Or z);
- */
-
- 70 # CNF(((_x) And (_y)) Or (_z)) <-- CNF(x Or z) And CNF(y Or z); // Distributing Or over And
- 80 # CNF((_x) Or ((_y) And (_z))) <-- CNF(x Or y) And CNF(x Or z);
-
- 90 # CNF((_x) And (_y)) <-- CNF(x) And CNF(y); // Transform subexpression
-
- 101 # CNF( (_x) < (_y) ) <-- Not CNFInEq(x >= y);
- 102 # CNF( (_x) > (_y) ) <-- CNFInEq(x > y);
- 103 # CNF( (_x) >= (_y) ) <-- CNFInEq(x >= y);
- 104 # CNF( (_x) <= (_y) ) <-- Not CNFInEq(x > y);
- 105 # CNF( (_x) == (_y) ) <-- CNFInEq(x == y);
- 106 # CNF( (_x) !== (_y) ) <-- Not CNFInEq(x == y);
-
- 111 # CNF( Not((_x) < (_y)) ) <-- CNFInEq( x >= y );
- 113 # CNF( Not((_x) <= (_y)) ) <-- CNFInEq( x > y );
- 116 # CNF( Not((_x) !== (_y)) ) <-- CNFInEq( x == y );
-
- /* Accept as fully simplified, fallthrough case */
- 200 # CNF(_x) <-- x;
-
- 20 # CNFInEq((_xex) == (_yex)) <-- (CNFInEqSimplify(xex-yex) == 0);
- 20 # CNFInEq((_xex) > (_yex)) <-- (CNFInEqSimplify(xex-yex) > 0);
- 20 # CNFInEq((_xex) >= (_yex)) <-- (CNFInEqSimplify(xex-yex) >= 0);
- 30 # CNFInEq(_exp) <-- (CNFInEqSimplify(exp));
-
- 10 # CNFInEqSimplify((_x) - (_x)) <-- 0; // strictly speaking, this is not always valid, i.e. 1/0 - 1/0 != 0...
- 100# CNFInEqSimplify(_x) <-- [/*Echo({"Hit the bottom of CNFInEqSimplify with ", x, Nl()});*/ x;];
- // former "Simplify";
-
- // Some shortcuts to match prev interface
- CanProveAux(_proposition) <-- LogicSimplify(proposition, 3);
- 10 # LogicSimplify(_proposition, _level)_(level<2) <-- CNF(proposition);
-
- 20 # LogicSimplify(_proposition, _level) <--
- [
- Local(cnf, list, clauses);
- Check(level > 1, "Wrong level");
- // First get the CNF version of the proposition
- Set(cnf, CNF(proposition));
-
- If(level <= 1, cnf, [
- Set(list, Flatten(cnf, "And"));
- Set(clauses, {});
- ForEach(clause, list)
- [
- Local(newclause);
- //newclause := BubbleSort(LogicRemoveTautologies(Flatten(clause, "Or")), LessThan);
- Set(newclause, LogicRemoveTautologies(Flatten(clause, "Or")));
- If(newclause != {True}, DestructiveAppend(clauses, newclause));
- ];
-
- /*
- Note that we sort each of the clauses so that they look the same,
- i.e. if we have (A And B) And ( B And A), only the first one will
- persist.
- */
- Set(clauses, RemoveDuplicates(clauses));
-
- If(Equals(level, 3) And (Length(clauses) != 0), [
- Set(clauses, DoUnitSubsumptionAndResolution(clauses));
- Set(clauses, LogicCombine(clauses));
- ]);
-
- Set(clauses, RemoveDuplicates(clauses));
-
- If(Equals(Length(clauses), 0), True, [
- /* assemble the result back into a boolean expression */
- Local(result);
- Set(result, True);
- ForEach(item,clauses)
- [
- Set(result, result And UnFlatten(item, "Or", False));
- ];
-
- result;
- ]);
- ]);
- ];
-
- /* CanProve tries to prove that the negation of the negation of
- the proposition is true. Negating twice is just a trick to
- allow all the simplification rules a la De Morgan to operate
- */
- /*CanProve(_proposition) <-- CanProveAux( Not CanProveAux( Not proposition));*/
-
- CanProve(_proposition) <-- CanProveAux( proposition );
-
- 1 # SimpleNegate(Not (_x)) <-- x;
- 2 # SimpleNegate(_x) <-- Not(x);
-
- /* LogicRemoveTautologies scans a list representing e1 Or e2 Or ... to find
- if there are elements p and Not p in the list. This signifies p Or Not p,
- which is always True. These pairs are removed. Another function that is used
- is RemoveDuplicates, which converts p Or p into p.
- */
-
- /* this can be optimized to walk through the lists a bit more efficiently and also take
- care of duplicates in one pass */
- LocalCmp(_e1, _e2) <-- LessThan(ToString() Write(e1), ToString() Write(e2));
-
- // we may want to add other expression simplifers for new expression types
- 100 # SimplifyExpression(_x) <-- x;
-
- // Return values:
- // {True} means True
- // {} means False
- LogicRemoveTautologies(_e) <--
- [
- Local(i, len, negationfound); Set(len, Length(e));
- Set(negationfound, False);
-
- //Echo(e);
- e := BubbleSort(e, "LocalCmp");
-
- For(Set(i, 1), (i <= len) And (Not negationfound), i++)
- [
- Local(x, n, j);
- // we can register other simplification rules for expressions
- //e[i] := MathNth(e,i) /:: {gamma(_y) <- SimplifyExpression(gamma(y))};
- Set(x, MathNth(e,i));
- Set(n, SimpleNegate(x)); /* this is all we have to do because of
- the kind of expressions we can have coming in */
-
- For(Set(j, i+1), (j <= len) And (Not negationfound), j++) [
- Local(y);
- Set(y, MathNth(e,j));
-
- If(Equals(y, n),
- [
- //Echo({"Deleting from ", e, " i=", i, ", j=", j, Nl()});
-
- Set(negationfound, True);
- //Echo({"Removing clause ", i, Nl()});
- ],
- If(Equals(y, x),
- [
- //Echo({"Deleting from ", e, " j=", j, Nl()});
- DestructiveDelete(e, j);
- Set(len,MathSubtract(len,1));
- ])
- );
- ];
- Check(len = Length(e), "The length computation is incorrect");
- ];
-
- If(negationfound, {True}, e); /* note that a list is returned */
- ];
-
- 10 # Contradict((_x) - (_y) == 0, (_x) - (_z) == 0)_(y != z) <-- True;
- 12 # Contradict((_x) == (_y), (_x) == (_z))_(y != z) <-- True;
- 13 # Contradict((_x) - (_y) == 0, (_x) - (_z) >= 0)_(z > y) <-- True;
- 14 # Contradict((_x) - (_y) == 0, (_x) - (_z) > 0)_(z > y) <-- True;
- 14 # Contradict(Not (_x) - (_y) >= 0, (_x) - (_z) > 0)_(z > y) <-- True;
- 15 # Contradict(_a, _b) <-- Equals(SimpleNegate(a), b);
-
- /* find the number of the list that contains n in it, a pointer to a list of lists in passed */
- LogicFindWith(_list, _i, _n) <--
- [
- Local(result, index, j);
- Set(result, -1); Set(index, -1);
-
- For(j := i+1, (result<0) And (j <= Length(list)), j++)
- [
- Local(k, len); Set(len, Length(list[j]));
- For(k := 1, (result<0) And (k<=len), k++)
- [
- Local(el); Set(el, list[j][k]);
-
- If(Contradict(n, el),
- [Set(result, j); Set(index, k);]);
- ];
- ];
- {result, index};
- ];
-
- /* LogicCombine is responsible for scanning a list of lists, which represent
- a form (p1 Or p2 Or ...) And (q1 Or q2 Or ...) And ... by scanning the lists
- for combinations x Or Y And Not x Or Z <-- Y Or Z . If Y Or Z is empty then this clause
- is false, and thus the entire proposition is false.
- */
- LogicCombine(_list) <--
- [
- Local(i, j);
- For(Set(i,1), i<=Length(list), Set(i,MathAdd(i,1)))
- [
- //Echo({"list[", i, "/", Length(list), "]: ", list[i], Nl()});
- Local(redo);
- Set(redo, False);
-
- For(j := 1, Not(redo) And (j<=Length(list[i])), j++)
- [
- Local(tocombine, n, k);
- Set(n, list[i][j]);
-
- {tocombine, k} := LogicFindWith(list, i, n);// search forward for n, tocombine is the list we
- // will combine the current one with
- If(tocombine != -1,
- [
- Local(combination);
- Check(k != -1, "k is -1");
-
- DestructiveDelete(list[i], j);
- DestructiveDelete(list[tocombine], k);
-
- Set(combination, LogicRemoveTautologies(Concat(list[i], list[tocombine])));
- DestructiveDelete(list, tocombine); // delete tocombine from the list, it's represented
- // by combination, which will be list[i]
- DestructiveReplace(list, i, combination);
-
- If(combination = {False}, // false is represented as {}
- DestructiveReplace(list, i, combination));
-
- If(combination = {}, // the combined clause is false, so the whole thing is false
- [Set(list, {{}}); Set(i, Length(list)+1);], [Set(i, 0);]);
-
- Set(redo, True); // this signals that we should break out of the inner loop
- ]);
- ];
- ];
- list;
- ];
-
- 10 # Subsumes((_x) - (_y) == 0, Not ((_x) - (_z)==0))_(y!=z) <-- True;
- // suif_tmp0_127_1-72==0 And 78-suif_tmp0_127_1>=0
- 20 # Subsumes((_x) - (_y) == 0, (_z) - (_x) >= 0)_(z>=y) <-- True;
- 20 # Subsumes((_x) - (_y) == 0, (_z) - (_x) > 0)_(z>y) <-- True;
- // suif_tmp0_127_1-72==0 And suif_tmp0_127_1-63>=0
- 30 # Subsumes((_x) - (_y) == 0, (_x) - (_z) >= 0)_(y>=z) <-- True;
- 30 # Subsumes((_x) - (_y) == 0, (_x) - (_z) > 0)_(y>z) <-- True;
-
- 90 # Subsumes((_x), (_x)) <-- True;
-
- 100# Subsumes((_x), (_y)) <-- False;
-
-
- // perform unit subsumption and resolutiuon for a unit clause # i
- // a boolean indicated whether there was a change is returned
- DoUnitSubsumptionAndResolution(_list) <--
- [
- Local(i, isFalse, isTrue, changed);
- Set(isFalse, False);
- Set(isTrue, False);
- Set(changed, True);
-
- //Echo({"In DoUnitSubsumptionAndResolution", Nl()});
-
- While(changed) [
- Set(changed, False);
-
- For(i:=1, (Not isFalse And Not isTrue) And i <= Length(list), i++)
- [
- If(Length(list[i]) = 1, [
- Local(x); Set(x, list[i][1]); //n := SimpleNegate(x);
- //Echo({"Unit clause ", x, Nl()});
-
- // found a unit clause, {x}, not use it to modify other clauses
- For(j:=1, (Not isFalse And Not isTrue) And j <= Length(list), j++)
- [
- If(i !=j, [
- Local(deletedClause); Set(deletedClause, False);
- For(k:=1, (Not isFalse And Not isTrue And Not deletedClause) And k <= Length(list[j]), k++)
- [
- // In both of these, if a clause becomes empty, the whole thing is False
-
- //Echo({" ", x, " subsumes ", list[j][k], i,j, Subsumes(x, list[j][k]), Nl()});
-
- // unit subsumption -- this kills clause j
- If(Subsumes(x, list[j][k]), [
- // delete this clause
- DestructiveDelete(list, j);
- j--;
- If(i>j, i--); // i also needs to be decremented
- Set(deletedClause, True);
- Set(changed, True);
- If(Length(list) = 0, [Set(isTrue, True);]);
- ],
- // else, try unit resolution
- If(Contradict(x, list[j][k]), [
- //Echo({x, " contradicts", list[j][k], Nl()});
- DestructiveDelete(list[j], k);
- k--;
- Set(changed, True);
- If(Length(list[j]) = 0, [Set(isFalse, True);]);
- ])
- );
- ];
- ]);
- ];
- ]);
- ];
- ];
-
- list;
- ];